package cn.huapu.power.energy.mapper;


import cn.huapu.power.energy.vo.Page;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 采用分页拦截器执行物理分页查询时，原生的Executor创建cacheKey时未能包含分页参数page，
 * 为了解决这个问题，创建了本拦截器拦截query方法，在创建cacheKey时将分页参数page包含其中。
 * 同时进行动态的设置分页sql和查询count总数
 * @author zdl
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class})})
public class PageInterceptor implements Interceptor {
	private static String defaultPageSqlId = "Page";

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[0];
		String id = ms.getId();
		if (id.endsWith(defaultPageSqlId)) {//以page结尾,执行分页逻辑
			Object parameter = args[1];
			RowBounds rowBounds = (RowBounds) args[2];
			ResultHandler resultHandler = (ResultHandler) args[3];
			Executor executor = (Executor) invocation.getTarget();
			BoundSql boundSql= ms.getBoundSql(parameter);
			CacheKey cacheKey= executor.createCacheKey(ms, parameter, rowBounds, boundSql);
			Page page = (Page) ((Map) parameter).get("page");
			if(page.getTotalCount() == 0L){
				long count = executeAutoCount(executor, newCountMappedStatement(ms,id+"_count"), parameter, boundSql, rowBounds, resultHandler);
				page.setTotalCount(count);
			}
			cacheKey.update(page.getPage());
			cacheKey.update(page.getRows());
			String pagesql = buildPageSql(boundSql.getSql(),page);
			boundSql = new BoundSql(ms.getConfiguration(), pagesql, boundSql.getParameterMappings(),
					parameter);
			return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
		}
		return invocation.proceed();
	}

	/**
	 * 拦截所有query
	 */
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}
	private String buildPageSql(String sql, Page page) {
        if (page != null) {
        	String tmpsql = sql.toLowerCase();
            // 增加动态排序的功能
            if (!tmpsql.contains("order by") && (page.getSort()!= null && !page.getSort().equals(""))
            		&& (page.getOrder()!= null && !page.getOrder().equals(""))) {
                sql += " order by " + page.getSort() + " " + page.getOrder();
            }
           sql += " limit " + page.getLimit() + "," + page.getRows();
        }
        return sql;
    }
	private long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql,
			RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
		CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
		// 调用方言获取 count sql
		String sql = boundSql.getSql();
		sql = sql.split("order[\\s]+by")[0];
        int idx = sql.indexOf(" from ");//在写mapper写sql的时候一定要注意from前后都应该有一个空格
		String countSql = "select count(*) from " + sql.substring(idx+5);
		// countKey.update(countSql);
		BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(),
				parameter);
		// 执行 count 查询
		Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey,
				countBoundSql);
		/****************jzx*****************/
		long count;
		if(((List) countResultList).size()>1){
			count = ((List) countResultList).size();
		}else if(((List) countResultList).size()==0){
			count = 0;
		}else{
			count = (long) ((List) countResultList).get(0);
		}
		/******************jzx*****************/

//		long count = (long) ((List) countResultList).get(0);
		return count;
	}
	private static final List<ResultMapping> EMPTY_RESULTMAPPING = new ArrayList<ResultMapping>(0);
	public static MappedStatement newCountMappedStatement(MappedStatement ms, String newMsId) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), newMsId, ms.getSqlSource(), ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : ms.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        //count查询返回值int
        List<ResultMap> resultMaps = new ArrayList<ResultMap>();
        ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId(), Long.class, EMPTY_RESULTMAPPING).build();
        resultMaps.add(resultMap);
        builder.resultMaps(resultMaps);
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
}
